using System;
using NUnit.Framework;
using Nuwanda.TfsSpotlight.Data;
using Nuwanda.TfsSpotlight.Services.Interfaces;
using Nuwanda.TfsSpotlight.Shell.Services.Interfaces;
using Nuwanda.TfsSpotlight.TFS;
using Rhino.Mocks;

namespace TfsSpotlight.Tests.TFS
{
    [TestFixture]
    public class TfsServerManagerTests
    {
        private MockRepository _mocks;
        private ITeamFoundationServerFactory _teamFoundationServerFactoryMock;
        private Microsoft.TeamFoundation.Client.ICredentialsProvider _credentialsProviderMock;
        private IMonitService _monitServiceMock;
        private ILogger _loggerMock;
        private IWorker _workerMock;
        private IHistoryService _historyServiceMock;

        private TfsServerManagerTestableStub _testSubject;

        [SetUp]
        public void SetUp()
        {
            this._mocks = new MockRepository();
            this._teamFoundationServerFactoryMock = this._mocks.StrictMock<ITeamFoundationServerFactory>();
            this._credentialsProviderMock =
                this._mocks.StrictMock<Microsoft.TeamFoundation.Client.ICredentialsProvider>();
            this._monitServiceMock = this._mocks.StrictMock<IMonitService>();
            this._loggerMock = this._mocks.StrictMock<ILogger>();
            this._workerMock = this._mocks.StrictMock<IWorker>();
            this._historyServiceMock = this._mocks.StrictMock<IHistoryService>();

            this._testSubject = new TfsServerManagerTestableStub(
                this._teamFoundationServerFactoryMock, this._credentialsProviderMock,
                this._monitServiceMock, this._loggerMock, this._workerMock, this._historyServiceMock);
        }
        
        [Test, ExpectedException(typeof(InvalidOperationException))]
        public void WhenAskedTo_Connect_And_ThereIsNoServerDescriptorSet_Should_ThrowInvalidOperationEception()
        {
            this._testSubject.Connect();
        }

        [Test]
        public void WhenAskedTo_Connect_And_ThereWasNoConnectionBefore_Should_GetServer_And_Connect()
        {
            var url = "url";
            var serverDescriptor = new ServerDescriptor("name", url);

            var teamFoundationServerMock = this._mocks.StrictMock<ITeamFoundationServer>();
            var eventSubscriber = this._mocks.StrictMock<IEventSubscriber>();

            using (this._mocks.Record())
            {
                Expect.Call(this._workerMock.BeginWork()).Return(true);
                this._testSubject.Connecting += eventSubscriber.HandleConnecting;
                eventSubscriber.HandleConnecting(this._testSubject, EventArgs.Empty);

                Expect.Call(this._teamFoundationServerFactoryMock.GetServer(url, this._credentialsProviderMock)).Return(teamFoundationServerMock);
                teamFoundationServerMock.Authenticate();

                this._testSubject.Connected += eventSubscriber.HandleConnected;
                eventSubscriber.HandleConnected(null, null);
                LastCall.IgnoreArguments().Do(new Action<object, EventArgs>(
                                delegate(object sender, EventArgs e)
                                    {
                                        Assert.AreEqual(this._testSubject, sender);
                                    }));

                this._workerMock.EndWork();
            }

            using (this._mocks.Playback())
            {
                this._testSubject.ServerDescriptor = serverDescriptor;
                this._testSubject.Connect();
            }
        }

        [Test]
        public void WhenAskedTo_Connect_And_ThereWasAlreadyAConnection_And_IsAuthenticated_Should_Return()
        {
            var url = "url";
            var serverDescriptor = new ServerDescriptor("name", url);
            var teamFoundationServerMock = this._mocks.StrictMock<ITeamFoundationServer>();
            var eventSubscriber = this._mocks.StrictMock<IEventSubscriber>();

            using (this._mocks.Record())
            {
                Expect.Call(this._workerMock.BeginWork()).Return(true);
                this._testSubject.Connecting += eventSubscriber.HandleConnecting;
                eventSubscriber.HandleConnecting(this._testSubject, EventArgs.Empty);

                Expect.Call(teamFoundationServerMock.HasAuthenticated).Return(true);

                this._testSubject.Connected += eventSubscriber.HandleConnected;
                eventSubscriber.HandleConnected(null, null);
                LastCall.IgnoreArguments().Do(new Action<object, EventArgs>(
                                delegate(object sender, EventArgs e)
                                {
                                    Assert.AreEqual(this._testSubject, sender);
                                }));
                this._workerMock.EndWork();
            }

            using (this._mocks.Playback())
            {
                this._testSubject.Test_Server = teamFoundationServerMock;
                this._testSubject.ServerDescriptor = serverDescriptor;
                this._testSubject.Connect();
            }
        }

        [Test]
        public void WhenAskedTo_Connect_And_ThereWasAlreadyAConnection_And_IsNotAuthemticated_Should_Authenticate()
        {
            var url = "url";
            var serverDescriptor = new ServerDescriptor("name", url);
            var teamFoundationServerMock = this._mocks.StrictMock<ITeamFoundationServer>();
            var eventSubscriber = this._mocks.StrictMock<IEventSubscriber>();

            using (this._mocks.Record())
            {
                Expect.Call(this._workerMock.BeginWork()).Return(true);
                this._testSubject.Connecting += eventSubscriber.HandleConnecting;
                eventSubscriber.HandleConnecting(this._testSubject, EventArgs.Empty);

                Expect.Call(teamFoundationServerMock.HasAuthenticated).Return(false);
                teamFoundationServerMock.Authenticate();

                this._testSubject.Connected += eventSubscriber.HandleConnected;
                eventSubscriber.HandleConnected(null, null);
                LastCall.IgnoreArguments().Do(new Action<object, EventArgs>(
                                delegate(object sender, EventArgs e)
                                {
                                    Assert.AreEqual(this._testSubject, sender);
                                }));
                this._workerMock.EndWork();
            }

            using (this._mocks.Playback())
            {
                this._testSubject.Test_Server = teamFoundationServerMock;
                this._testSubject.ServerDescriptor = serverDescriptor;
                this._testSubject.Connect();
            }
        }

        //TODO: Test exceptions


        public interface IEventSubscriber
        {
            void HandleConnecting(object sender, EventArgs e);
            void HandleConnected(object sender, EventArgs e);
        }
    }
}